﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Ultima.Xna.Data;
using Ultima.Xna.Extensions;
using Ultima.Xna.Graphics.UI.Gumps;

namespace Ultima.Xna.Graphics.UI
{
    public class GumpRenderer : IUIRenderer
    {
        private static ExtendedSpriteBatch _spriteBatch;
        private static Effect _effect;
        private static GraphicsDevice _graphicsDevice;

        public GumpRenderer(Game game)
        {
            if (_effect == null)
            {
                _effect = game.Content.Load<Effect>("Shaders\\Gumps");
            }

            if (_spriteBatch == null)
            {
                _spriteBatch = new ExtendedSpriteBatch(game.GraphicsDevice);
            }

            if (_graphicsDevice == null)
            {
                _graphicsDevice = game.GraphicsDevice;
            }
        }

        public void Begin()
        {
            Begin(SpriteBlendMode.AlphaBlend);
        }

        public void Begin(SpriteBlendMode blendMode)
        {
            Begin(blendMode, SpriteSortMode.Deferred, SaveStateMode.None, Matrix.Identity);
        }
        public void Begin(SpriteBlendMode blendMode, SpriteSortMode sortMode, SaveStateMode stateMode)
        {
            Begin(blendMode, sortMode, stateMode, Matrix.Identity);
        }
        public void Begin(SpriteBlendMode blendMode, SpriteSortMode sortMode, SaveStateMode stateMode, Matrix transformMatrix)
        {
            _spriteBatch.Shader = _effect;
            _spriteBatch.Begin(blendMode, sortMode, stateMode, transformMatrix);
        }
        public void End()
        {
            _spriteBatch.End();
        }

        public void BeginHue(int hue)
        {
            _spriteBatch.End();

            if (_spriteBatch.Shader != null)
            {
                _spriteBatch.Shader.Parameters["HueTexture"].SetValue(Hues.Texture);
                _spriteBatch.Shader.Parameters["Hue"].SetValue(new Vector2(hue - 1, 0));
            }

            _spriteBatch.BeginLast();
        }
        public void EndHue()
        {
            if (_spriteBatch.Shader != null)
            {
                _spriteBatch.Shader.Parameters["Hue"].SetValue(new Vector2(-1, 0));
            }

            _spriteBatch.End();
            _spriteBatch.BeginLast();
        }

        public void Render(GumpLabel item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            _graphicsDevice.ScissorRectangle = item.RenderBounds;
            _graphicsDevice.RenderState.ScissorTestEnable = true;

            BeginHue(item.Hue);

            ASCIIFont font = item.Font;
            string text = item.Text;
            Vector2 position = item.RenderPosition;
            Rectangle bounds = item.RenderBounds;
            Vector2 fontSize = new Vector2(font.GetWidth(text), (font.Height * (text.CountChar('\n') + 1)));

            switch (item.TextAlignment)
            {
                case TextAlignment.TopLeft:
                    position += new Vector2(0, 0);
                    break;
                case TextAlignment.TopMiddle:
                    position += new Vector2((float)bounds.Width / 2 - (float)bounds.X / 2, 0);
                    break;
                case TextAlignment.TopRight:
                    position += new Vector2((float)bounds.Width - (float)bounds.X, 0);
                    break;
                case TextAlignment.MiddleLeft:
                    position += new Vector2(0, (float)bounds.Height / 2 - fontSize.Y / 2);
                    break;
                case TextAlignment.MiddleMiddle:
                    position += new Vector2((float)bounds.Width / 2 - fontSize.X / 2, (float)bounds.Height / 2 - fontSize.Y / 2);
                    break;
                case TextAlignment.MiddleRight:
                    position += new Vector2((float)bounds.Width - fontSize.X, (float)bounds.Height / 2 - fontSize.Y / 2);
                    break;
                case TextAlignment.BottomLeft:
                    position += new Vector2(0, (float)bounds.Height - fontSize.Y);
                    break;
                case TextAlignment.BottomMiddle:
                    position += new Vector2((float)bounds.Width / 2 - fontSize.X / 2, (float)bounds.Height - fontSize.Y);
                    break;
                case TextAlignment.BottomRight:
                    position += new Vector2((float)bounds.Width - fontSize.X, (float)bounds.Height - fontSize.Y);
                    break;
            }

            if (position.X < item.RenderPosition.X)
                position.X = item.RenderPosition.X;

            if (position.Y < item.RenderPosition.Y)
                position.Y = item.RenderPosition.Y;

            float dx = 0;
            float height = font.Height;

            string[] split = text.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

            for (int i = 0; i < split.Length; i++)
            {
                dx = 0;

                for (int x = 0; x < split[i].Length; x++)
                {
                    _spriteBatch.Draw(font.GetTexture(split[i][x]), position + new Vector2(dx, i * font.Height), Color.White);
                    dx += font.GetWidth(split[i][x].ToString());
                }
            }

            EndHue();

            _graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            _graphicsDevice.RenderState.ScissorTestEnable = false;
        }

        public void Render(GumpShadowedLabel item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            if (item.ClipToParent && item.Parent != null)
                _graphicsDevice.ScissorRectangle = item.Parent.RenderBounds;
            else
                _graphicsDevice.ScissorRectangle = item.RenderBounds;

            _graphicsDevice.RenderState.ScissorTestEnable = true;

            BeginHue(item.Hue);

            ASCIIFont font = item.Font;
            string text = item.Text;
            Vector2 position = item.RenderPosition;
            Rectangle bounds = item.RenderBounds;
            Vector2 fontSize = new Vector2(font.GetWidth(text), (font.Height * (text.CountChar('\n') + 1)));

            switch (item.TextAlignment)
            {
                case TextAlignment.TopLeft:
                    position += new Vector2(0, 0);
                    break;
                case TextAlignment.TopMiddle:
                    position += new Vector2((float)bounds.Width / 2 - (float)bounds.X / 2, 0);
                    break;
                case TextAlignment.TopRight:
                    position += new Vector2((float)bounds.Width - (float)bounds.X, 0);
                    break;
                case TextAlignment.MiddleLeft:
                    position += new Vector2(0, (float)bounds.Height / 2 - fontSize.Y / 2);
                    break;
                case TextAlignment.MiddleMiddle:
                    position += new Vector2((float)bounds.Width / 2 - fontSize.X / 2, (float)bounds.Height / 2 - fontSize.Y / 2);
                    break;
                case TextAlignment.MiddleRight:
                    position += new Vector2((float)bounds.Width - fontSize.X, (float)bounds.Height / 2 - fontSize.Y / 2);
                    break;
                case TextAlignment.BottomLeft:
                    position += new Vector2(0, (float)bounds.Height - fontSize.Y);
                    break;
                case TextAlignment.BottomMiddle:
                    position += new Vector2((float)bounds.Width / 2 - fontSize.X / 2, (float)bounds.Height - fontSize.Y);
                    break;
                case TextAlignment.BottomRight:
                    position += new Vector2((float)bounds.Width - fontSize.X, (float)bounds.Height - fontSize.Y);
                    break;
            }

            if (position.X < item.RenderPosition.X)
                position.X = item.RenderPosition.X;

            if (position.Y < item.RenderPosition.Y)
                position.Y = item.RenderPosition.Y;

            BeginHue(item.ShadowHue);

            float dx = 0;
            float height = font.Height;

            string[] split = text.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

            for (int i = 0; i < split.Length; i++)
            {
                dx = 0;

                for (int x = 0; x < split[i].Length; x++)
                {
                    _spriteBatch.Draw(font.GetTexture(split[i][x]), position + item.ShadowOffset + new Vector2(dx, i * font.Height), Color.White);
                    dx += font.GetWidth(split[i][x].ToString());
                }
            }

            EndHue();

            BeginHue(item.Hue);

            dx = 0;

            for (int i = 0; i < split.Length; i++)
            {
                dx = 0;

                for (int x = 0; x < split[i].Length; x++)
                {
                    _spriteBatch.Draw(font.GetTexture(split[i][x]), position + new Vector2(dx, i * font.Height), Color.White);
                    dx += font.GetWidth(split[i][x].ToString());
                }
            }

            EndHue();

            _graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            _graphicsDevice.RenderState.ScissorTestEnable = false;
        }

        public void Render(GumpAlpha item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            //_graphicsDevice.ScissorRectangle = item.RenderBounds;
            //_graphicsDevice.RenderState.ScissorTestEnable = true;

            Rectangle bounds = item.RenderBounds;

            _spriteBatch.Draw(item.Manager.SceneTexture, bounds, bounds, new Color(new Vector4(1.0f, 1.0f, 1.0f, 0.65f)));

            //_graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            //_graphicsDevice.RenderState.ScissorTestEnable = false;
        }

        public void Render(GumpBackground item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            _graphicsDevice.ScissorRectangle = item.RenderBounds;
            _graphicsDevice.RenderState.ScissorTestEnable = true;

            Vector2 position = item.RenderPosition;
            Vector2 size = item.RenderSize;

            int x = (int)position.X;
            int y = (int)position.Y;
            int leftWidth = item.MiddleLeftTexture.Width;
            int rightWidth = item.MiddleRightTexture.Width;
            int topHeight = item.TopTexture.Height;
            int bottomHeight = item.BottomTexture.Height;
            int width = (int)size.X;
            int height = (int)size.Y;

            Rectangle[] sourceRectangles = new Rectangle[] {
                new Rectangle(0, 0, leftWidth - 2, topHeight),//topleft
                new Rectangle(0, 0, width - (rightWidth + leftWidth) + 4, topHeight),//topmiddle
                new Rectangle(0, 0, rightWidth - 1, topHeight),//topright
                new Rectangle(0, 0, leftWidth, height - (topHeight + bottomHeight)),//midleft
                new Rectangle(0, 0, width - (leftWidth + rightWidth) + 4, height - (topHeight + bottomHeight)),//midmid
                new Rectangle(0, 0, rightWidth - 1, height - (topHeight + bottomHeight)),//midright
                new Rectangle(0, 0, leftWidth - 2, bottomHeight),//botleft
                new Rectangle(0, 0, width - (leftWidth + rightWidth) + 4, bottomHeight),//botmiddle
                new Rectangle(0, 0, rightWidth - 1, bottomHeight)//botright
            };

            //Topleft
            _spriteBatch.Draw(item.TopLeftTexture, position, sourceRectangles[0], Color.White);
            //Top
            _spriteBatch.Draw(item.TopTexture, position + new Vector2(leftWidth - 2, 0), sourceRectangles[1], Color.White);
            //Topright
            _spriteBatch.Draw(item.TopRightTexture, position + new Vector2(width - (rightWidth - 2), 0), sourceRectangles[2], Color.White);
            //Left
            _spriteBatch.Draw(item.MiddleLeftTexture, position + new Vector2(0, topHeight), sourceRectangles[3], Color.White);
            //Middle
            _spriteBatch.Draw(item.MiddleTexture, position + new Vector2(leftWidth, topHeight), sourceRectangles[4], Color.White);
            //Right
            _spriteBatch.Draw(item.MiddleRightTexture, position + new Vector2(width - rightWidth + 2, topHeight), sourceRectangles[5], Color.White);
            //Bottomleft
            _spriteBatch.Draw(item.BottomLeftTexture, position + new Vector2(0, height - bottomHeight), sourceRectangles[6], Color.White);
            //Bottom
            _spriteBatch.Draw(item.BottomTexture, position + new Vector2(leftWidth - 2, height - bottomHeight), sourceRectangles[7], Color.White);
            //Bottomright
            _spriteBatch.Draw(item.BottomRightTexture, position + new Vector2(width - (rightWidth - 2), height - bottomHeight), sourceRectangles[8], Color.White);

            _graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            _graphicsDevice.RenderState.ScissorTestEnable = false;
        }

        public void Render(GumpButton item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            //_graphicsDevice.ScissorRectangle = item.RenderBounds;
            //_graphicsDevice.RenderState.ScissorTestEnable = true;

            Texture2D texture = item.NormalTexture;

            if (item.Pressed)
                texture = item.PressedTexture;

            _spriteBatch.Draw(texture, item.RenderBounds, Color.White);

            //_graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            //_graphicsDevice.RenderState.ScissorTestEnable = false;
        }

        public void Render(GumpCheckbox item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            //_graphicsDevice.ScissorRectangle = item.RenderBounds;
            //_graphicsDevice.RenderState.ScissorTestEnable = true;

            Texture2D texture = item.UncheckedTexture;

            if (item.Checked)
                texture = item.CheckedTexture;

            _spriteBatch.Draw(texture, item.RenderPosition, Color.White);

            //_graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            //_graphicsDevice.RenderState.ScissorTestEnable = false;
        }

        public void Render(GumpExpander item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            //_graphicsDevice.ScissorRectangle = item.RenderBounds;
            //_graphicsDevice.RenderState.ScissorTestEnable = true;


            //_graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            //_graphicsDevice.RenderState.ScissorTestEnable = false;
        }

        public void Render(GumpHtml item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            //_graphicsDevice.ScissorRectangle = item.RenderBounds;
            //_graphicsDevice.RenderState.ScissorTestEnable = true;
            

            //_graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            //_graphicsDevice.RenderState.ScissorTestEnable = false;
        }

        public void Render(GumpImage item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            //_graphicsDevice.ScissorRectangle = item.RenderBounds;
            //_graphicsDevice.RenderState.ScissorTestEnable = true;

            BeginHue(item.Hue);

            _spriteBatch.Draw(item.Texture, item.RenderPosition, Color.White);

            EndHue();

            //_graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            //_graphicsDevice.RenderState.ScissorTestEnable = false;
        }

        public void Render(GumpRadio item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            //_graphicsDevice.ScissorRectangle = item.RenderBounds;
            //_graphicsDevice.RenderState.ScissorTestEnable = true;

            Texture2D texture = item.UncheckedTexture;

            if (item.Checked)
                texture = item.CheckedTexture;

            _spriteBatch.Draw(texture, item.RenderPosition, Color.White);

            //_graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            //_graphicsDevice.RenderState.ScissorTestEnable = false;
        }

        public void Render(GumpScrollbar item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            //_graphicsDevice.ScissorRectangle = item.RenderBounds;
            //_graphicsDevice.RenderState.ScissorTestEnable = true;
            
            Vector2 position = item.RenderPosition;
            Rectangle bounds = item.RenderBounds;

            _spriteBatch.Draw(item.BgTexture, bounds, new Rectangle(0, 0, bounds.Width, bounds.Height), Color.White);
            _spriteBatch.Draw(item.UpArrowTexture, new Vector2(position.X, position.Y), Color.White);
            _spriteBatch.Draw(item.DownArrowTexture, new Vector2(position.X, position.Y + bounds.Height - item.DownArrowTexture.Height), Color.White);


            //_graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            //_graphicsDevice.RenderState.ScissorTestEnable = false;
        }

        public void Render(GumpTextEntry item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            _graphicsDevice.ScissorRectangle = item.RenderBounds;
            _graphicsDevice.RenderState.ScissorTestEnable = true;

            BeginHue(item.Hue);

            ASCIIFont font = item.Font;
            string text = item.Text + (item.HasFocus ? "_" : "");
            Vector2 position = item.RenderPosition;
            Rectangle bounds = item.RenderBounds;
            Vector2 fontSize = new Vector2(font.GetWidth(text), (font.Height * (text.CountChar('\n') + 1)));

            switch (item.TextAlignment)
            {
                case TextAlignment.TopLeft:
                    position += new Vector2(0, 0);
                    break;
                case TextAlignment.TopMiddle:
                    position += new Vector2((float)bounds.Width / 2 - (float)bounds.X / 2, 0);
                    break;
                case TextAlignment.TopRight:
                    position += new Vector2((float)bounds.Width - (float)bounds.X, 0);
                    break;
                case TextAlignment.MiddleLeft:
                    position += new Vector2(0, (float)bounds.Height / 2 - fontSize.Y / 2);
                    break;
                case TextAlignment.MiddleMiddle:
                    position += new Vector2((float)bounds.Width / 2 - fontSize.X / 2, (float)bounds.Height / 2 - fontSize.Y / 2);
                    break;
                case TextAlignment.MiddleRight:
                    position += new Vector2((float)bounds.Width - fontSize.X, (float)bounds.Height / 2 - fontSize.Y / 2);
                    break;
                case TextAlignment.BottomLeft:
                    position += new Vector2(0, (float)bounds.Height - fontSize.Y);
                    break;
                case TextAlignment.BottomMiddle:
                    position += new Vector2((float)bounds.Width / 2 - fontSize.X / 2, (float)bounds.Height - fontSize.Y);
                    break;
                case TextAlignment.BottomRight:
                    position += new Vector2((float)bounds.Width - fontSize.X, (float)bounds.Height - fontSize.Y);
                    break;
            }

            if (position.X < item.RenderPosition.X)
                position.X = item.RenderPosition.X;

            if (position.Y < item.RenderPosition.Y)
                position.Y = item.RenderPosition.Y;

            float dx = 0;
            float height = font.Height;

            string[] split = text.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

            for (int i = 0; i < split.Length; i++)
            {
                dx = 0;

                for (int x = 0; x < split[i].Length; x++)
                {
                    _spriteBatch.Draw(font.GetTexture(split[i][x]), position + new Vector2(dx, i * font.Height), Color.White);
                    dx += font.GetWidth(split[i][x].ToString());
                }
            }

            EndHue();

            _graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            _graphicsDevice.RenderState.ScissorTestEnable = false;
        }

        public void Render(GumpTiledImage item)
        {
            PresentationParameters pp = _graphicsDevice.PresentationParameters;

            //_graphicsDevice.ScissorRectangle = item.RenderBounds;
            //_graphicsDevice.RenderState.ScissorTestEnable = true;

            BeginHue(item.Hue);

            _spriteBatch.Draw(item.Texture, item.RenderPosition, item.RenderBounds, Color.White);

            EndHue();

            //_graphicsDevice.ScissorRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
            //_graphicsDevice.RenderState.ScissorTestEnable = false;
        }
    }
}
